package control.networking;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import model.*;
import model.AI.Cyborg;
import model.Player.PlayerTurn;

/**
 * RiskAIClient is the backend connection to the AI players
 * the humans will play against.
 * @author Kevin Luikens
 *
 */
public class RiskAIClient extends Thread implements Observer {
	private 		Player 	player;
	private 		int 	port;
	
	private static 	Socket 	server;
	
	private 		Map		map;
	private 		Map		mapBackup;
	
	private	static	ObjectOutputStream oos;
	private			ObjectInputStream ois;
	
	
	
	/**
	 * Initializes a new RiskAIClient, taking in its player and the port
	 * on which it will connect to the server on.
	 */
	public RiskAIClient( Cyborg p, int port) {
		System.out.println("RiskAIClient: Inside constructor");

		this.port = port;
		map = null;
		mapBackup = null;
		player = (Player) p;
	}		
		

		
	/**
	 * Processes the generic object received by the
	 * ObjectInputStream read and determines what instance of a class it
	 * is, and takes an accordant action based upon such.
	 * @param obj The generic object with which readObject() returns. Determines
	 * what class it is an instance of.
	 * @throws InterruptedException
	 */
	private void processObject( Object obj ) throws InterruptedException {

		if ( obj instanceof Player ) {	//	if UUID matches yours, it's your turn. else, wait for new map.
			System.out.println("RC- Received Next Player");
			if ( ((Player) obj).getUUID().compareTo(player.getUUID()) == 0 ) { //<--- TODO Remove semicolon to prevent hours of frustration! 
				System.out.println("R-AI-C :    now its my turn "+ player);
				player.setTurnPhase(PlayerTurn.START);
				player.addObserver(this);
				((Cyborg)player).run();
				
			}	
			else {}
		}
		
		if ( obj instanceof Map ) {
			setThisPlayer((Map) obj);
			if ( map == null ) {
				mapBackup = map;				
				map = (Map) obj;
			}
			else {
				if(player.getTurnPhase() == PlayerTurn.FALSE){
					System.out.println("AI map update, not my turn");
					player.setMap((Map)obj);
				}
				else {
					System.out.println("AI my turn, not updating");
					// its my turn, AI doesn't need to update Map, its current Map is up to date 
				}
			}
		}		
	}
	
	/**
	 * Used privately to modify the Map that the player contains
	 * when necessary.
	 * @param map Map object
	 */
	private void setThisPlayer(Map map){
		Iterator<Player> it = map.getPlayerList().iterator();
		while(it.hasNext()){
			Player temp = it.next();
			if(temp.getUUID().compareTo(player.getUUID()) == 0){
				player = temp;
			}
		}
	}
	
	/**
	 * Used to write the a new modified Map to the server when
	 * the AI client takes a new action in the game.
	 * @param map Map object
	 */
	private void updateWithNewMap(Map map){
		mapBackup = map;
		this.map = map;
		try {
			System.out.println("OOS try to write " + map);
			oos.writeObject( map );
			oos.flush();
			System.out.println("OOS wrote");
		} catch (IOException e) {
			e.printStackTrace();	}
	}

	/**
	 * Creates a shell class that'll notify
	 * the server that this client's turn is done.
	 * @param p Player object
	 */
	private void endMyTurn(Player p){
		p.setTurnPhase(PlayerTurn.FALSE);
		try {
			oos.writeObject( new TurnDone(p) );
			oos.flush();
		} catch (IOException e) {
			e.printStackTrace();	}
	}

	/**
	 * Returns the player that this client represents.
	 * @return Player object
	 */
	public Player getThisPlayer(){
		return player;
	}
	
	/**
	 * Represents the main logic of this client. It will be invoked
	 * automatically when the Thread has been started.
	 */
	public void run() {
		boolean connected = false;
	
		try {
			while(!connected) {
				server = new Socket( "localhost", port);
				oos = new ObjectOutputStream( server.getOutputStream() );
				ois = new ObjectInputStream( server.getInputStream() );
				oos.writeObject(player);
				System.out.println("RAIC server constructed "+server);
				connected = true;
			}
		} catch (ConnectException e) {
			System.out.println("RiskAIClient: ConnectionException");
				try {
					sleep(2000);
					run();
				} catch (InterruptedException e1) {
					e.printStackTrace();
				} 
			} catch (IOException e) {
			e.printStackTrace();	}
			
			while (true) {
			try {
				processObject( ois.readObject() );
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		}
	}

	/**
	 * Called when the Player it is observing has
	 * taken an action that requires updating of the Map to the
	 * rest of the gamers.
	 */
	public void update(Observable o, Object arg) {
		if(arg instanceof String){
				System.out.println("AI update "+ arg);
				if(((String) arg).compareTo("NewMap") == 0){
					this.updateWithNewMap(((Player)o).getMap());
				}
				else if (((String) arg).compareTo("EndTurn") == 0){
					this.endMyTurn((Player)o);
				}
		}
	}
}
